package cn.okcode.api.framework;

import cn.okcode.api.framework.result.MineType;
import cn.okcode.api.framework.result.ResultHandler;
import cn.okcode.api.framework.dispatcher.IHandlerResolver;
import cn.okcode.api.framework.dispatcher.ParamDefinition;
import cn.okcode.api.framework.interceptor.InterceptorHandler;

import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/**
 * 调用方法信息
 *
 * @author pyk13
 */
public class Callee {
    public static final Logger logger = LogManager.getLogger(Callee.class.getName());

    private final Object controller;
    private final Method method;
    private final String clazzName;
    private final String methodName;
    private final ResultHandler resultHandler;
    private ParamDefinition[] paramDefinitions;
    private MineType mineType;
    private boolean accessEnforced;
    private boolean transactionOpen;
    private IHandlerResolver handlerResolver;
    private boolean cache;

    public Callee(Object controller, Method method, ResultHandler resultHandler) {
        this.controller = controller;
        this.method = method;
        this.clazzName = controller.getClass().getName();
        this.methodName = method.getName();
        this.resultHandler = resultHandler;
    }

    public String call(ActionContext context) {
        String result;
        try {
            InterceptorHandler.defaultHandler.beforeInvoke(context);

            Object data = method.invoke(controller, context.getParameters());

            context.setResult(data);

            InterceptorHandler.defaultHandler.afterInvoke(context);

            result = resultHandler.ok(data);
        } catch (IllegalAccessException e) {
            throw new ActionException("方法调用出错,非法访问", e);
        } catch (InvocationTargetException e) {
            Throwable targetEx = e.getTargetException() == null ? e : e.getTargetException();
            context.setThrowable(targetEx);
            InterceptorHandler.defaultHandler.onException(context);
            result = resultHandler.err(this, "", targetEx);
        }
        if (mineType != MineType.DEFAULT) {
            context.getResponse().setContentType(mineType.toString());
        }
        return result;
    }

    public String getClazzSimpleName() {
        int i = clazzName.lastIndexOf('.');
        return clazzName.substring(i + 1);
    }

    public String getClazzName() {
        return clazzName;
    }

    public String getMethodName() {
        return methodName;
    }

    public String getCallee() {
        return clazzName + "." + methodName;
    }

    public String getSignature() {
        List<String> strParamTypes = new ArrayList<>(paramDefinitions.length);
        for (ParamDefinition paramDefinition : paramDefinitions) {
            strParamTypes.add(paramDefinition.getParameterType().getName());
        }
        return String.format("%s.%s(%s)", clazzName, methodName, StringUtils.join(strParamTypes, ","));
    }

    public Object getController() {
        return controller;
    }

    public Method getMethod() {
        return method;
    }

    public ResultHandler getResultHandler() {
        return resultHandler;
    }

    public MineType getMineType() {
        return mineType;
    }

    public ParamDefinition[] getParamDefinitions() {
        return paramDefinitions;
    }

    public void setParamDefinitions(ParamDefinition[] paramDefinitions) {
        this.paramDefinitions = paramDefinitions;
    }

    public void setMineType(MineType mineType) {
        this.mineType = mineType;
    }

    public IHandlerResolver getHandlerResolver() {
        return handlerResolver;
    }

    public void setHandlerResolver(IHandlerResolver handlerResolver) {
        this.handlerResolver = handlerResolver;
    }

    public boolean isCache() {
        return cache;
    }

    public void setCache(boolean cache) {
        this.cache = cache;
    }

    public boolean isAccessEnforced() {
        return accessEnforced;
    }

    public void setAccessEnforced(boolean accessEnforced) {
        this.accessEnforced = accessEnforced;
    }

    public boolean isTransactionOpen() {
        return transactionOpen;
    }

    public void setTransactionOpen(boolean transactionOpen) {
        this.transactionOpen = transactionOpen;
    }

    @Override
    public String toString() {
        return getSignature();
    }
}
